<template>
  <!-- 虚拟列表容器 -->
  <div ref="virtualListRef" class="virtual-list-container" @scroll="scrollEvent">

    <!-- 虚拟列表占位 -->
    <div class="virtual-list-placeholder" :style="{ height: listHeight + 'px' }"></div>

    <!-- 虚拟列表 -->
    <div class="virtual-list" :style="{ transform: getTransform }">
      <div
          v-for="item in visibleData"
          :data-id="item.id"
          :key="item.id"
          :style="{ height: itemSize + 'px', width: '100%' }"
      >
        <!-- 列表项插槽 -->
        <slot :value="item.value" :id="item.id"></slot>
      </div>
    </div>

  </div>
</template>

<script setup>
import {computed, onMounted, reactive, ref} from "vue";

// 虚拟列表
const virtualListRef = ref();

// 组件传参
const props = defineProps({
  // 所有列表数据
  listData: {
    type: Array,
    default: () => []
  },
  // 每项高度
  itemSize: {
    type: Number,
    default: 40
  }
})

// 绑定数据
const data = reactive({
  // 可视区域高度
  screenHeight: 0,
  // 偏移量
  startOffset: 0,
  // 起始索引
  start: 0,
  // 结束索引
  end: 0
})

// 列表总高度
const listHeight = computed(() => {
  return props.listData.length * props.itemSize
})

// 可显示的列表项数
const visibleCount = computed(() => {
  return Math.ceil(data.screenHeight / props.itemSize)
})

// 偏移量对应的style
const getTransform = computed(() => {
  return `translate3d(0,${data.startOffset}px,0)`
})

// 获取真实显示列表数据
const visibleData = computed(() => {
  return props.listData.slice(data.start, Math.min(data.end, props.listData.length))
})

// 滚动事件处理函数
const scrollEvent = () => {
  // 当前滚动位置
  const scrollTop = virtualListRef.value.scrollTop
  // 此时的开始索引
  data.start = Math.floor(scrollTop / props.itemSize)
  // 此时的结束索引
  data.end = data.start + visibleCount.value
  // 此时的偏移量
  data.startOffset = scrollTop - (scrollTop % props.itemSize)
}

// 挂载完毕
onMounted(() => {
  data.screenHeight = virtualListRef.value.clientHeight
  data.start = 0
  data.end = visibleCount.value
})

</script>

<style scoped>
.virtual-list-container {
  overflow: auto;
  position: relative;
  -webkit-overflow-scrolling: touch;
  height: 100%;
  width: 100%;
}

.virtual-list-placeholder {
  position: absolute;
  left: 0;
  right: 0;
  z-index: -1;
}

.virtual-list {
  text-align: center;
}

</style>
